package org.example.util;

import org.example.model.BaseModel;
import org.example.model.BasePo;
import org.example.model.PageInfo;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

public class ModelMapperUtil {
    /**
     * 构造page对象
     * @param poList
     * @param pageNum
     * @param pageSize
     * @param total
     * @param modelClass
     * @param <T>
     * @return
     */
    public static <T extends BaseModel> PageInfo<T> mapToPage(List<BasePo> poList,long pageNum, long pageSize, long total, Class<T> modelClass) {
        PageInfo<T> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(pageNum);
        pageInfo.setPageSize(pageSize);
        pageInfo.setTotal(total);
        if(!CollectionUtils.isEmpty(poList)){
            pageInfo.setList(poList.stream().map(o->mapToModel(o,modelClass)).collect(Collectors.toList()));
        }
        pageInfo.setPages((int) Math.ceil((double) total / pageSize));
        return pageInfo;
    }

    /**
     * Map po to model
     * @param po
     * @param modelClass
     * @return
     */
    public static <T extends BaseModel> T mapToModel(BasePo po, Class<T> modelClass) {
        try {
            T model = (T) modelClass.newInstance();
            mapFields(po, model, modelClass);
            return model;
        } catch (Exception e) {
            throw new RuntimeException("Failed to map PO to Model", e);
        }
    }

    /**
     * Map model to po
     * @param model
     * @param poClass
     * @return
     */
    public static <S extends BasePo> S mapToPo(BaseModel model, Class<S> poClass) {
        try {
            S po = poClass.newInstance();
            mapFields(model, po, poClass);
            return po;
        } catch (Exception e) {
            throw new RuntimeException("Failed to map Model to PO", e);
        }
    }

    /**
     * 过滤掉private static final属性
     * @param field
     * @return
     */
    private static boolean filterCanAccessField(Field field) {
        //过滤掉final属性
        if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) {
            return false;
        }
        //过滤掉static属性
        if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
            return false;
        }
        //过滤掉transient属性
        if (java.lang.reflect.Modifier.isTransient(field.getModifiers())) {
            return false;
        }
        return true;
    }

    /**
     * 映射属性
     * @param source
     * @param target
     * @param targetClass
     * @throws Exception
     */
    private static void mapFields(Object source, Object target, Class<?> targetClass) throws Exception {
        for (Field field : targetClass.getDeclaredFields()) {
            if (!filterCanAccessField(field)){
                continue;
            }
            Field sourceField=findField(source.getClass(),field.getName());
            if(null==sourceField){
                continue;
            }
            //校验并处理isDeleted属性
            if (checkAndProcessIsDeletedField(source, sourceField,target, field)) {
                continue;
            }

            field.setAccessible(true);
            sourceField.setAccessible(true);
            field.set(target, sourceField.get(source));
        }

        if (targetClass.getSuperclass() != null) {
            mapFields(source, target, targetClass.getSuperclass());
        }
    }

    /**
     * 获取类属性
     * @param clazz
     * @return
     */
    public static Field findField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
        while (clazz != null) {
            try {
                // 尝试获取当前类中的字段
                Field field = clazz.getDeclaredField(fieldName);
                return field;
            } catch (NoSuchFieldException e) {
                // 如果当前类中没有找到，则继续在父类中查找
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }

    /**
     * 处理isDeleted属性
     * @param source
     * @param sourceField
     * @param target
     * @param targetField
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private static boolean checkAndProcessIsDeletedField(Object source,Field sourceField, Object target, Field targetField) throws NoSuchFieldException, IllegalAccessException {
        if (!"isDeleted".equals(targetField.getName())) {
            return false;
        }
        //判断source该属性是否为String类型
        if (sourceField.getType() == String.class) {
            targetField.setAccessible(true);
            sourceField.setAccessible(true);
            //校验target该属性是否为Boolean类型
            if (targetField.getType() == Boolean.class) {
                targetField.set(target, "y".equals(sourceField.get(source)));
            } else {
                targetField.set(target, sourceField.get(source));
            }
            return true;
        }
        //判断source该属性是否为Boolean类型
        if (sourceField.getType() == Boolean.class) {
            targetField.setAccessible(true);
            sourceField.setAccessible(true);
            //校验target该属性是否为String类型
            if (targetField.getType() == String.class) {
                targetField.set(target, (Boolean) sourceField.get(source) ? "y" : "n");
            } else {
                targetField.set(target, sourceField.get(source));
            }
            return true;
        }
        return false;
    }
}
